#include "Mesh.hpp"
#include <set>
#include <fstream>
#include "MeshAlgorithm.hpp"
#include "SmeshFile.hpp"
using namespace std;


void SmeshFile::Read(const std::string& aFileName,
                      Mesh&              aMesh)
{
   aMesh.faces->clear();
   aMesh.verts->clear();
   ifstream f(aFileName.c_str());
   int vertCount, garb;
   f >> vertCount >> garb >> garb >> garb;
   aMesh.verts->resize(vertCount);

   for (size_t i = 0; i < aMesh.verts.size(); ++i)
   {
      Vec3& v = aMesh.verts[i];
      f >> garb >> v.x >> v.y >> v.z;
   }
   size_t faceCount;
   f >> faceCount;
   aMesh.faces->resize(faceCount);
   for (size_t i = 0; i < faceCount; ++i)
   {
      Face& face = aMesh.faces[i];
      f >> garb >> face.vertex[0] >> face.vertex[1] >> face.vertex[2];
      face.materials[1] = 1;
      face.materials[0] = 1;
      --face.vertex[0];
      --face.vertex[1];
      --face.vertex[2];
   }
}

// Pick 1 face from each 'region interface'.  These are used to pick points that tetgen uses
// to fill the mesh with region/material information.
void SmeshFile::FindFaceSamples(const Mesh&       aMesh,
                                std::vector<int>& aFaces)
{
   MeshAlgorithm::AdjacencyMap adj;
   MeshAlgorithm::MakeAdjacencyMap(*aMesh.faces, adj);

   std::set<int> remainingFaces(boost::counting_iterator<int>(0), boost::counting_iterator<int>(aMesh.faces.size()));
   while (! remainingFaces.empty())
   {
      int f = *remainingFaces.begin();
      std::vector<int> start; start.push_back(f);
      std::vector<int> region;
      MeshAlgorithm::FillRegion(adj, *aMesh.faces, start, region);
      for (size_t i = 0; i < region.size(); ++i)
      {
         remainingFaces.erase(region[i]);
      }
      aFaces.push_back(f);
   }
}

void SmeshFile::Write(const std::string& aFileName,
                      const Mesh&        aMesh)
{
   Mesh mesh(aMesh);

   //const int cF1 = 3;
   //const int cF2 = 4;
   //for (size_t i = 0; i < mesh.faces.size(); ++i)
   //{
   //   Face& face = mesh.faces[i];
   //   if (face.materials[0] != cF1 && face.materials[0] != cF2)
   //   {
   //      face.materials[0] = -1;
   //   }
   //   if (face.materials[1] != cF1 && face.materials[1] != cF2)
   //   {
   //      face.materials[1] = -1;
   //   }
   //}

   std::set<int> materials;
   for (size_t i = 0; i < mesh.faces.size(); ++i)
   {
      int m1 = mesh.faces[i].materials[0];
      int m2 = mesh.faces[i].materials[1];
      if (m1 != -1) materials.insert(m1);
      if (m2 != -1) materials.insert(m2);
   }

   ofstream f(aFileName.c_str());

   // Output vertices
   f << mesh.verts.size() << " 3 0 0\n";
   for (size_t i = 0; i < mesh.verts.size(); ++i)
   {
      Vec3& v = mesh.verts[i];
      f << i+1 << ' ' << v.x << " " << v.y << " " << v.z << '\n';
   }

   typedef std::pair<int, Face> Facet;
   std::vector<Facet> facets;
   std::vector<Face> sampleFaces;
   for (std::set<int>::iterator i = materials.begin(); i != materials.end(); ++i)
   {
      bool storedFace = false;
      int mat = *i;
      for (size_t i = 0; i < mesh.faces.size(); ++i)
      {
         Face f = mesh.faces[i];
         int m1 = f.materials[0];
         int m2 = f.materials[1];
         if (m1 == mat)
         {
            facets.push_back(Facet(mat, f));
         }
         if (m2 == mat)
         {
            Face flip(f);
            std::swap(f.vertex[0], f.vertex[2]);
            facets.push_back(Facet(mat, flip));
         }
      }
   }

   std::vector<int> faceSamples;
   FindFaceSamples(aMesh, faceSamples);

   for (size_t i = 0; i < faceSamples.size(); ++i)
   {
      Face f = mesh.faces[faceSamples[i]];
      int m1 = f.materials[0];
      int m2 = f.materials[1];
      if (m1 != -1)
      {
         sampleFaces.push_back(f); 
         sampleFaces.back().materials[0] = m1; 
      }
      if (m2 != -1)
      {
         Face flip(f);
         std::swap(f.vertex[0], f.vertex[2]);
         sampleFaces.push_back(flip); 
         sampleFaces.back().materials[0] = m2;
      }
   }

   f << facets.size() << '\n'; 
   for (size_t i = 0; i < facets.size(); ++i)
   {
      Face& face = facets[i].second;
      f << 3 << ' ' << face.vertex[0]+1 << ' ' << face.vertex[1]+1 << ' ' << face.vertex[2]+1 << '\n';
   }
   f << "0\n";

   // Now output some points with material information so tetgen will populate the tet's material
   f << sampleFaces.size() << '\n';
   for (size_t i = 0; i < sampleFaces.size(); ++i)
   {
      Face& face = sampleFaces[i];
      Vec3 a = mesh.verts[face.vertex[0]];
      Vec3 b = mesh.verts[face.vertex[1]];
      Vec3 c = mesh.verts[face.vertex[2]];
      Vec3 into = cross((c-a), (b-a));
      Vec3 pt = (a+b+c) / 3.0f + (into * -.05f);
      f << i+1 << ' ' << pt[0] << ' ' << pt[1] << ' ' << pt[2] << ' ' << face.materials[0];
      f << '\n';
   }
   //for (size_t i = 0; i < mesh.verts.size(); ++i)
   //{
   //   Vec3& v = mesh.verts[i];
   //   f << i+1 << v.x << " " v.y << " " << v.z << '\n';
   //}


}
